/*
 * IdentityComonentDefinition.java
 *
 * Created on September 2, 2006, 4:37 PM
 *
 * (C) 2006 R. Alexander Milowski
 */

package org.infoset.component.library;

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.infoset.component.Component;
import org.infoset.component.ComponentBase;
import org.infoset.component.ComponentDefinitionBase;
import org.infoset.component.library.InsertComponentDefinition.InsertComponent;
import org.infoset.xml.Attribute;
import org.infoset.xml.Document;
import org.infoset.xml.Element;
import org.infoset.xml.Item;
import org.infoset.xml.ItemDestination;
import org.infoset.xml.Name;
import org.infoset.xml.XMLException;
import org.infoset.xml.util.DocumentDestination;
import org.infoset.xml.util.DocumentSource;

/**
 *
 * @author alex
 */
public class SetAttributesComponentDefinition extends ComponentDefinitionBase {
   
   static Logger LOG = Logger.getLogger(XSLTComponentDefinition.class.getName());
   static Name DOCUMENT_INPUT_PORT = Name.create("source");
   static Name ATTRIBUTES_INPUT_PORT = Name.create("attributes");
   static Name RESULT_OUTPUT_PORT = Name.create("result");
   static List<Name> inputs = new ArrayList<Name>();
   static List<Name> outputs = new ArrayList<Name>();
   static {
      inputs.add(DOCUMENT_INPUT_PORT);
      inputs.add(ATTRIBUTES_INPUT_PORT);
      outputs.add(RESULT_OUTPUT_PORT);
   }
   
   public class SetAttributesComponent extends ComponentBase {
      
      ItemDestination output;
      Document inputDocument;
      Map<Name,String> attributes;
      
      public SetAttributesComponent(Name name,String vendor,String version) {
         super(name,vendor,version);
         this.attributes = null;
         this.inputDocument = null;
      }
      
      public void init(Component.Context context)
         throws XMLException
      {
         super.init(context);
         this.attributes = null;
         this.inputDocument = null;
      }
      
      public void bindResource(Name port,Document document)
         throws XMLException 
      {
         if (port.equals(DOCUMENT_INPUT_PORT)) {
            inputDocument = document;
         } else if (port.equals(ATTRIBUTES_INPUT_PORT)) {
            attributes = new HashMap<Name,String>();
            for (Attribute att : document.getDocumentElement().getAttributes().values()) {
               attributes.put(att.getName(),att.getText());
            }
         } else {
            throw new XMLException("Binding port "+port+" via ItemSource is not supported.");
         }
      }
   
      public void bindResource(Name port,URI location)
         throws XMLException
      {
         if (port.equals(DOCUMENT_INPUT_PORT)) {
            try {
               inputDocument = context.getDocumentLoader().load(location);
            } catch (IOException ex) {
               throw new XMLException("Cannot load input document due to I/O error.",ex);
            }
         } else if (port.equals(ATTRIBUTES_INPUT_PORT)) {
            try {
               Document document = context.getDocumentLoader().load(location);
               attributes = new HashMap<Name,String>();
               for (Attribute att : document.getDocumentElement().getAttributes().values()) {
                  attributes.put(att.getName(),att.getText());
               }
            } catch (IOException ex) {
               throw new XMLException("Cannot load input document due to I/O error.",ex);
            }
         } else {
            super.bindResource(port,location);
         }
      }
      public void bindOutputPort(Name port,ItemDestination output)
         throws XMLException
      {
         if (port.equals(RESULT_OUTPUT_PORT)) {
            this.output = output;
         } else {
            throw new XMLException("Unknown output port "+port+" on "+name);
         }
      }

      
      public void onStart() 
         throws XMLException
      {
         if (attributes!=null && inputDocument!=null) {
            generateFromDocument();
         }
      }
      
      void generateFromDocument() 
         throws XMLException
      {
         Element root = inputDocument.getDocumentElement();
         for (Name name : attributes.keySet()) {
            root.setAttributeValue(name,attributes.get(name));
         }
         DocumentSource.generate(inputDocument,false,output);
      }
      
      public ItemDestination getInputPort(Name port) 
         throws XMLException
      {
         if (port.equals(DOCUMENT_INPUT_PORT)) {
            if (attributes!=null) {
               return new ItemDestination() {
                  int level = -1;
                  public void send(Item item)
                     throws XMLException 
                  {
                     switch (item.getType()) {
                        case ElementItem:
                           level++;
                           if (level==0) {
                              Element root = (Element)item;
                              for (Name name : attributes.keySet()) {
                                 root.setAttributeValue(name,attributes.get(name));
                              }
                           }
                           output.send(item);
                           break;
                        case ElementEndItem:
                           level--;
                           output.send(item);
                           break;
                        default:
                           output.send(item);
                     }
                  }
               };
            } else {
               return new DocumentDestination() {
                  public void send(Item item) 
                     throws XMLException
                  {
                     super.send(item);
                     if (item.getType()==Item.ItemType.DocumentEndItem) {
                        inputDocument = getDocument();
                        if (attributes!=null) {
                           generateFromDocument();
                        }
                     }
                  }
               };
            }
         } else if (port.equals(ATTRIBUTES_INPUT_PORT)) {
            return new ItemDestination() {
               boolean done = false;
               public void send(Item item) 
                  throws XMLException
               {
                  if (!done && item.getType()==Item.ItemType.ElementItem) {
                     attributes = new HashMap<Name,String>();
                     for (Attribute att : ((Element)item).getAttributes().values()) {
                        attributes.put(att.getName(),att.getText());
                     }
                     done = true;
                  }
               }
            };
         } else {
            super.getInputPort(port);
         }
         return null;
      }
      
      public void onDocumentInput(Name port,Document document) 
         throws XMLException
      {
         if (port.equals(DOCUMENT_INPUT_PORT)) {
            if (inputDocument!=null) {
               throw new XMLException("The input document has already be receieved.");
            }
            inputDocument = document;
            
            if (attributes!=null) {
               generateFromDocument();
            }
         } else if (port.equals(ATTRIBUTES_INPUT_PORT)) {
            attributes = new HashMap<Name,String>();
            for (Attribute att : document.getDocumentElement().getAttributes().values()) {
               attributes.put(att.getName(),att.getText());
            }
            if (inputDocument!=null) {
               generateFromDocument();
            }
         } else {
            super.getInputPort(port);
         }
      }
      
   }
   
   
   /** Creates a new instance of IdentityComonentDefinition */
   public SetAttributesComponentDefinition(Name name) {
      super(name,null,null,null);
   }
   
   /** Creates a new instance of IdentityComonentDefinition */
   public SetAttributesComponentDefinition(Name name,String vendor,String version,URI vendorLocation) {
      super(name,vendor,version,vendorLocation);
   }
   
   public Component newInstance() 
      throws XMLException
   {
      return new SetAttributesComponent(getName(),getVendor(),getVersion());
   }
   
   public Collection<Name> getInputs() {
      return inputs;
   }
   
   public Collection<Name> getOutputs() {
      return outputs;
   }

}
